use super::*;

/// https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Magic {
	TrueType,
	OpenType,
	Collection,
}
impl FromData for Magic {
	const SIZE: usize = 4;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		match u32::parse(data)? {
			0x00010000 | 0x74727565 => Some(Magic::TrueType),
			0x4F54544F => Some(Magic::OpenType),
			0x74746366 => Some(Magic::Collection),
			_ => None,
		}
	}
}

#[repr(transparent)]
#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default, Hash)]
pub struct GlyphId(pub u16);
impl FromData for GlyphId {
	const SIZE: usize = 2;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		u16::parse(data).map(GlyphId)
	}
}
impl core::fmt::Debug for GlyphId {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_fmt(format_args!("GlyphId({})", self.0))
	}
}

#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Tag(pub u32);
impl Tag {
	#[inline]
	pub const fn from_bytes(bytes: &[u8; 4]) -> Self {
		Tag(((bytes[0] as u32) << 24) | ((bytes[1] as u32) << 16) | ((bytes[2] as u32) << 8) | (bytes[3] as u32))
	}
	#[inline]
	pub const fn to_bytes(self) -> [u8; 4] {
		[
			(self.0 >> 24 & 0xff) as u8,
			(self.0 >> 16 & 0xff) as u8,
			(self.0 >> 8 & 0xff) as u8,
			(self.0 >> 0 & 0xff) as u8,
		]
	}
}
impl FromData for Tag {
	const SIZE: usize = 4;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		u32::parse(data).map(Tag)
	}
}
impl core::fmt::Debug for Tag {
	#[inline]
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
		let b = [
			(self.0 >> 24 & 0xff) as u8 as char,
			(self.0 >> 16 & 0xff) as u8 as char,
			(self.0 >> 8 & 0xff) as u8 as char,
			(self.0 >> 0 & 0xff) as u8 as char,
		];
		write!(
			f,
			"Tag({}{}{}{})",
			b.get(0).unwrap_or(&' '),
			b.get(1).unwrap_or(&' '),
			b.get(2).unwrap_or(&' '),
			b.get(3).unwrap_or(&' ')
		)
	}
}

// https://learn.microsoft.com/en-us/typography/opentype/spec/otff#data-types
#[derive(Clone, Copy, Debug)]
pub struct U24(pub u32);
impl FromData for U24 {
	const SIZE: usize = 3;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		let data: [u8; 3] = data.try_into().ok()?;
		Some(U24(u32::from_be_bytes([0, data[0], data[1], data[2]])))
	}
}

/// A 16-bit signed fixed number with the low 14 bits of fraction (2.14).
#[derive(Clone, Copy, Debug)]
pub struct F2DOT14(pub i16);
impl FromData for F2DOT14 {
	const SIZE: usize = 2;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		i16::parse(data).map(F2DOT14)
	}
}
impl F2DOT14 {
	#[inline]
	pub fn to_f32(self) -> f32 {
		f32::from(self.0) / 16384.0
	}
	#[inline]
	pub fn apply_float_delta(&self, delta: f32) -> f32 {
		self.to_f32() + (delta as f64 * (1.0 / 16384.0)) as f32
	}
}

/// A 32-bit signed fixed-point number (16.16).
#[derive(Clone, Copy, Default)]
pub struct Fixed(pub f32);
impl FromData for Fixed {
	const SIZE: usize = 4;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		i32::parse(data).map(|n| Fixed(n as f32 / 65536.0))
	}
}
impl core::fmt::Debug for Fixed {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(f, "Fixed({})", self.0)
	}
}
impl Fixed {
	#[inline]
	pub fn from_i32(v: i32) -> Self {
		Fixed(v as f32 / 65536.0)
	}
	#[inline]
	pub fn apply_float_delta(&self, delta: f32) -> f32 {
		self.0 + (delta as f64 * (1.0 / 65536.0)) as f32
	}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LongDateTime(pub i64);
impl FromData for LongDateTime {
	const SIZE: usize = 8;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		i64::parse(data).map(Self)
	}
}
impl core::fmt::Debug for LongDateTime {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(f, "Date({})", self.0)
	}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Version16Dot16(pub u32);
impl FromData for Version16Dot16 {
	const SIZE: usize = 4;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		u32::parse(data).map(Self)
	}
}
impl core::fmt::Debug for Version16Dot16 {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(f, "v{:08X}", self.0)
	}
}

pub type FWORD = i16;
pub type UFWORD = u16;
